//
//  Status.hpp
//  Clock Signal
//
//  Created by Thomas Harte on 25/11/2023.
//  Copyright © 2023 Thomas Harte. All rights reserved.
//

#pragma once

namespace Intel::i8272 {

enum class MainStatus: uint8_t {
	FDD0Seeking			= 0x01,
	FDD1Seeking			= 0x02,
	FDD2Seeking			= 0x04,
	FDD3Seeking			= 0x08,

	CommandInProgress	= 0x10,
	InNonDMAExecution	= 0x20,
	DataIsToProcessor	= 0x40,
	DataReady			= 0x80,
};

enum class Status0: uint8_t {
	NormalTermination	= 0x00,
	AbnormalTermination = 0x80,
	InvalidCommand		= 0x40,
	BecameNotReady		= 0xc0,

	SeekEnded			= 0x20,
	EquipmentFault		= 0x10,
	NotReady			= 0x08,

	HeadAddress			= 0x04,
	UnitSelect			= 0x03,
};

enum class Status1: uint8_t {
	EndOfCylinder		= 0x80,
	DataError			= 0x20,
	OverRun				= 0x10,
	NoData				= 0x04,
	NotWriteable		= 0x02,
	MissingAddressMark	= 0x01,
};

enum class Status2: uint8_t {
	DeletedControlMark		= 0x40,
	DataCRCError			= 0x20,
	WrongCyinder			= 0x10,
	ScanEqualHit			= 0x08,
	ScanNotSatisfied		= 0x04,
	BadCylinder				= 0x02,
	MissingDataAddressMark	= 0x01,
};

enum class Status3: uint8_t {
	Fault				= 0x80,
	WriteProtected		= 0x40,
	Ready				= 0x20,
	Track0				= 0x10,
	TwoSided			= 0x08,
	HeadAddress			= 0x04,
	UnitSelect			= 0x03,
};

class Status {
public:
	Status() {
		reset();
	}

	void reset() {
		main_status_ = 0;
		set(MainStatus::DataReady, true);
		status_[0] = status_[1] = status_[2] = 0;
	}

	/// @returns The main status register value.
	uint8_t main() const {
		return main_status_;
	}
	uint8_t operator [](const int index) const {
		return status_[index];
	}

	//
	// Flag setters.
	//
	void set(const MainStatus flag, const bool value) {
		set(uint8_t(flag), value, main_status_);
	}
	void start_seek(const int drive)		{	main_status_ |= 1 << drive;	}
	void set(const Status0 flag)			{	set(uint8_t(flag), true, status_[0]);	}
	void set(const Status1 flag)			{	set(uint8_t(flag), true, status_[1]);	}
	void set(const Status2 flag)			{	set(uint8_t(flag), true, status_[2]);	}

	void set_status0(const uint8_t value)	{	status_[0] = value;	}

	//
	// Flag getters.
	//
	bool get(const MainStatus flag)	{	return main_status_ & uint8_t(flag);	}
	bool get(const Status2 flag)	{	return status_[2] & uint8_t(flag);		}

	/// Begin execution of whatever @c CommandDecoder currently describes, setting internal
	/// state appropriately.
	void begin(const CommandDecoder &command) {
		set(MainStatus::DataReady, false);
		set(MainStatus::CommandInProgress, true);

		if(command.is_access()) {
			status_[0] = command.drive_head();
		}
	}

private:
	void set(const uint8_t flag, const bool value, uint8_t &target) {
		if(value) {
			target |= flag;
		} else {
			target &= ~flag;
		}
	}

	uint8_t main_status_;
	uint8_t status_[3];
};

}
